home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
src
/
apps
/
gmemusage
/
process.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
21KB
|
840 lines
/*
* process.c
*
* Code to collect information about memory usage in the system
*
* Copyright 1994, Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
#define _KMEMUSER
#include <sys/types.h>
#include <sys/procfs.h>
#include <sys/param.h>
#include <sys/sysmp.h>
#include <sys/sysinfo.h>
#include <sys/sysmacros.h>
#include <sys/sbd.h>
#include <sys/pfdat.h>
#include <sys/proc.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
#include "process.h"
#include "inode.h"
#define PROCDIR "/proc"
#define MAXMAPS 256
/*
* dirp is for opening /proc
*/
static DIR *dirp = NULL;
static unsigned long kpstart, ktext, ketext, kend, kpbase, kpfdat;
static unsigned long kpagesize;
static int pgsize; /* page size in 1000 of bytes (ie. 4, 16, etc...) */
/*
* void
* KernelInfo()
*
* Description:
* Initialize a few constant global kernel variables
*/
void
KernelInfo(void)
{
register int fd;
/* Only do once */
if (!kend) {
/* Get some info from sysmp() */
if ((kpstart = sysmp(MP_KERNADDR, MPKA_PSTART)) == -1)
perror("sysmp(MP_KERNADDR, MPKA_PSTART)");
kpstart = PHYS_TO_K0(kpstart);
if ((ktext = sysmp(MP_KERNADDR, MPKA_TEXT)) == -1)
perror("sysmp(MP_KERNADDR, MPKA_TEXT)");
if ((ketext = sysmp(MP_KERNADDR, MPKA_ETEXT)) == -1)
perror("sysmp(MP_KERNADDR, MPKA_ETEXT)");
if ((kend = sysmp(MP_KERNADDR, MPKA_END)) == -1)
perror("sysmp(MP_KERNADDR, MPKA_END)");
if ((kpfdat = sysmp(MP_KERNADDR, MPKA_PFDAT)) == -1)
perror("sysmp(MP_KERNADDR, MPKA_PFDAT)");
if ((kpbase = sysmp(MP_KERNADDR, MPKA_KPBASE)) == -1)
perror("sysmp(MP_KERNADDR, MPKA_KPBASE)");
/* Read some stuff from /dev/kmem */
if (fd = open("/dev/kmem", 0)) {
/* Get value of KPBASE */
if (lseek(fd, kpbase, SEEK_SET) == -1)
perror("lseek");
if (read(fd, (caddr_t)&kpbase, sizeof kpbase) < 0)
perror("read");
kpfdat += btoct(kpbase) * sizeof (pfd_t);
kpbase = PHYS_TO_K0(kpbase);
close(fd);
}
}
}
/*
* static PROGRAM *
* AddElem(PROGRAM *head, char *progName, char *mapName, char *mapType,
* long privSize, long weightSize, long resSize, long size,
* pid_t pid, int markNProc)
*
* Description:
* Add an element to our program list.
*
* Parameters:
* head The list to add this element to
* progName Name of program to add
* privSize Size of private pages
* weightSize Weighted memusage size
* resSize resident size of program to add
* size total size of program to add
* pid process id of program to add
* markNProc 1 if we care about keeping nProc up to date
*
* Returns:
* A new list (the element being added may go at the beginning)
*/
static PROGRAM *
AddElem(PROGRAM *head, char *progName, char *mapName, char *mapType,
long privSize, long weightSize, long resSize,
long size, pid_t pid, int markNProc)
{
PROGRAM *elem, *prev, *pidPlace;
int cmp;
elem = head;
prev = NULL;
pidPlace = NULL;
/*
* Look for another copy of the same program already running
*/
while (elem && (cmp = strcmp(progName, elem->progName)) != 0) {
/*
* Remember where the program should go (we sort by pid) in
* case this is the first of its kind that we've found
*/
if (pid < elem->pid && !pidPlace) {
pidPlace = prev;
}
prev = elem;
elem = elem->next;
}
if (elem && cmp == 0) {
/*
* Another copy is running; just add the new one's stats to
* the old one
*/
elem->privSize += privSize;
elem->weightSize += weightSize;
elem->resSize += resSize;
elem->size += size;
if (markNProc) {
elem->nProc++;
}
} else {
/*
* A new program is encountered. Allocate a new element, and
* insert it into the list.
*/
elem = malloc(sizeof *elem);
/*
* progName and mapName get strduped because the point to
* things like prinfo structs that change. mapType points to
* static storage that does not change. Be very careful if
* you change this, because FreeUsage (in this file) knows
* about this, AND SO DOES DrawSetup (in draw.c)!!!
*/
elem->progName = strdup(progName);
elem->mapName = mapName ? strdup(mapName) : NULL;
elem->mapType = mapType; /* Don't need to dup this; it points */
/* to static storage that won't change */
elem->privSize = privSize;
elem->weightSize = weightSize;
elem->resSize = resSize;
elem->size = size;
elem->pid = pid;
elem->nProc = 1;
elem->print = 1;
if (!prev) {
elem->prev = NULL;
elem->next = head;
head = elem;
} else {
if (pidPlace) {
prev = pidPlace;
}
elem->next = prev->next;
elem->prev = prev;
prev->next = elem;
if (elem->next) {
elem->next->prev = elem;
}
}
}
return head;
}
/*
* static PROGRAM *
* SortList(PROGRAM *head, int flag)
*
* Description:
* Sort the element list by physical size (largest first)
*
* Parameters:
* head Start of the double linked list of elements
*
* Returns:
* New head of the list
*/
static PROGRAM *
SortList(PROGRAM *head, int flag)
{
PROGRAM *new = NULL, *prev, *walk, *next, *scan;
/* Walk the original list */
for (walk = head; walk != NULL; walk = next) {
/* Correct weightSize values */
if (flag != 0) {
walk->weightSize += MA_WSIZE_FRAC - 1;
walk->weightSize /= MA_WSIZE_FRAC;
}
/* Get it now, will be rewritten below */
next = walk->next;
/* No sort, just scale */
if (flag < 0) {
new = head;
continue;
}
/* Put into new sorted list */
for (prev = NULL, scan = new; scan != NULL;
scan = scan->next) {
if (walk->weightSize > scan->weightSize)
break;
prev = scan;
}
/* Insert into new list before current element */
if (scan) {
walk->next = scan;
walk->prev = scan->prev;
scan->prev = walk;
if (prev)
prev->next = walk;
else
new = walk;
} else if (new) {
walk->prev = prev;
prev->next = walk;
walk->next = NULL;
} else {
new = walk;
walk->prev = NULL;
walk->next = NULL;
}
}
return new;
}
/*
* Indentify RLD bss extents
*/
isrldbss(struct prmap_sgi *map)
{
if (((unsigned)map->pr_vaddr >= 0x0fbc0000) &&
((unsigned)map->pr_vaddr < 0x0fc40000))
return 1;
return 0;
}
/*
* Indentify /dev/zero
*/
isdevzero(struct prmap_sgi *map)
{
static dev_t zdev;
static ino_t zino;
/* Lookup /dev/zero */
if (!zdev) {
FindInode("zero", &zdev, &zino);
}
/* Match? */
if (map->pr_dev == zdev && map->pr_ino == zino)
return 1;
return 0;
}
/*
* char *
* MapFlags(prmap_sgi_t *map)
*
* Description:
* Translate the flags for a region into a meaningful word that
* describes what type of region it is
*
* Parameters:
* map region pointer
*
* Returns:
* string describing the type of region
*/
char *
MapFlags(prmap_sgi_t *map)
{
if (map->pr_mflags & MA_PHYS) {
return "Physical Device";
} else if (map->pr_mflags & MA_STACK) {
return "Stack";
} else if (map->pr_mflags & MA_BREAK) {
return "Break";
} else if (map->pr_mflags & MA_EXEC) {
return "Text";
} else if (map->pr_mflags & MA_COW) {
return "Data";
} else if (map->pr_mflags & MA_SHMEM) {
return "Shmem";
} else if ((map->pr_mflags & (MA_READ | MA_WRITE)) ==
(MA_READ | MA_WRITE)) {
return "RW";
} else if (map->pr_mflags & MA_READ) {
return "RO";
}
return "Other";
}
/*
* int
* PteAccounting(prmap_sgi_t *maps, int nmaps)
*
* Description:
* Try and figure out how many page tables are in core
*
* Parameters:
* maps region pointer
* nmaps number of maps in region list
*/
PteAccounting(prmap_sgi_t *maps, int nmaps)
{
unsigned pteblksize = (kpagesize / sizeof (caddr_t)) * kpagesize;
prmap_sgi_t *map;
long segno, refcnt, lrefcnt = 0, space = 0;
caddr_t vaddr, vend;
int i, last = -1;
/* Search all the segments */
for (map = maps, i = 0; i < nmaps; ++map, ++i) {
/* Skip empty ones */
if (map->pr_vsize == 0)
continue;
/* Walk segment one segment at a time (XXX fix me) */
vaddr = map->pr_vaddr;
vend = map->pr_vaddr + map->pr_size;
refcnt = map->pr_mflags >> MA_REFCNT_SHIFT;
while (vaddr < vend) {
/* If not same as last time & incore, then bill me */
segno = (long)vaddr / pteblksize;
if (last == -1)
last = segno;
if (last != segno) {
space += kpagesize / lrefcnt;
last = segno;
lrefcnt = 0;
}
vaddr += pteblksize;
if (!lrefcnt || (refcnt < lrefcnt)) {
lrefcnt = refcnt;
}
}
}
if (lrefcnt) {
space += kpagesize / lrefcnt;
}
return space / 1024;
}
/*
* Indentify PROGRAM text extent
*/
static int
isprgtxt(struct prmap_sgi *map)
{
if ((map->pr_mflags & (MA_EXEC|MA_PRIMARY)) == (MA_PRIMARY|MA_EXEC))
return 1;
return 0;
}
/*
* Indentify RLD text extent
*/
static int
isrldtxt(struct prmap_sgi *map)
{
if ((unsigned)map->pr_vaddr == 0x0fb60000)
return 1;
return 0;
}
/*
* static void OpenProcDir(void)
*
* Description:
* Open /proc if it's not open, and rewind. dirp is a global
* variable.
*/
static void OpenProcDir(void)
{
/* Get system page size */
if (!kpagesize)
kpagesize = getpagesize();
pgsize = kpagesize/1024;
if (!dirp) {
if ((dirp = opendir(PROCDIR)) == NULL) {
perror(PROCDIR);
exit(1);
}
}
rewinddir(dirp);
}
/*
* void GetObjInfo(char *objName, PROGRAM **all, PROGRAM **objp)
*
* Description:
* Get physical memory usage information for all mapped objects
* in the system.
*
* Parameters:
* objName name of object to get detailed information for
* all gets usage for all objectss
* objp gets usage for a specific object
*/
void GetObjInfo(char *objName, PROGRAM **all, PROGRAM **objp)
{
struct dirent *dent;
char procFile[MAXPATHLEN];
int status, fd, nmaps, i, pid = 1, ppid = 1, mino;
register struct prmap_sgi *map, *amap, *rmap, *bmap;
struct prmap_sgi maps[MAXMAPS];
prmap_sgi_arg_t maparg;
PROGRAM *list = NULL;
PROGRAM *plist = NULL;
unsigned long refCount;
double rss;
char *mname, *flagName;
struct prpsinfo info;
long uspace = 0, ptespace = 0, mem;
maparg.pr_vaddr = (caddr_t)maps;
maparg.pr_size = sizeof maps;
OpenProcDir();
while ((dent = readdir(dirp)) != NULL) {
if (!isdigit(dent->d_name[0]))
continue;
sprintf(procFile, "%s/%s", PROCDIR, dent->d_name);
status = (fd = open(procFile, O_RDONLY)) != -1
&& ioctl(fd, PIOCPSINFO, &info) != -1
&& (nmaps = ioctl(fd, PIOCMAP_SGI, &maparg)) != -1;
close(fd);
if (!status) {
continue;
}
/* Figure out if U-area is in core and charge process for it */
uspace += (info.pr_flag & SULOAD) ? (USIZE * kpagesize / 1024) : 0;
/* Try and figure out how many page tables are resident in memory */
ptespace += PteAccounting(maps, nmaps);
amap = NULL;
rmap = NULL;
/* Search regions to find app segment & rld segment */
for (map = maps, i = nmaps; i-- > 0; ++map) {
/* Hack for app region */
if (!amap && isprgtxt(map)) {
amap = map;
}
/* Remember primary BRK region */
if ((map->pr_mflags & (MA_PRIMARY|MA_BREAK)) ==
(MA_PRIMARY|MA_BREAK)) {
bmap = map;
}
/* Remember RLD region */
if (!rmap && isrldtxt(map)) {
rmap = map;
}
}
/* Scan the table */
for (map = maps, i = nmaps; i-- > 0; ++map) {
if (map->pr_mflags & MA_PHYS) {
continue;
}
/* Try and bill /dev/zero regions to real owners */
if (isdevzero(map)) {
/* Hack for rld's /dev/zero regions */
if (rmap && isrldbss(map)) {
map->pr_dev = rmap->pr_dev;
map->pr_ino = rmap->pr_ino;
map->pr_mflags |= MA_BREAK;
}
/* Otherwise bill to apps primary break region */
else if (bmap) {
map->pr_dev = bmap->pr_dev;
map->pr_ino = bmap->pr_ino;
map->pr_mflags |= MA_BREAK;
}
} else if (amap && ((map->pr_mflags & MA_STACK) ||
(map->pr_mflags & MA_BREAK))) {
map->pr_dev = amap->pr_dev;
map->pr_ino = amap->pr_ino;
}
if (map->pr_dev == amap->pr_dev && map->pr_ino == amap->pr_ino) {
mname = info.pr_fname;
} else {
mname = map->pr_ino ?
InodeLookup(map->pr_dev, map->pr_ino) : NULL;
}
flagName = MapFlags(map);
refCount = map->pr_mflags >> MA_REFCNT_SHIFT;
rss = (double)map->pr_wsize;
rss *= pgsize; /* use 1KB resolution */
rss /= (double)refCount;
list = AddElem(list, mname ? mname : flagName,
mname, flagName,
(map->pr_psize * pgsize) / refCount, rss,
0, 0, pid++, 0);
if (objName && mname && strcmp(objName, mname) == 0) {
plist = AddElem(plist, flagName, mname, flagName,
(map->pr_psize * pgsize) / refCount, rss,
0, 0, ppid++, 0);
}
}
}
list = SortList(list, 1);
if (plist) plist = SortList(plist, -1);
mem = uspace + ptespace;
list = AddElem(list, "U areas & PTEs", "U areas & PTES", IRIX,
mem, mem, 0, 0,
pid++, 0);
*all = list;
*objp = plist;
}
/*
* void
* GetProcInfo(char *procName, PROGRAM **all, PROGRAM **proc)
*
* Description:
* Get memory usage information for all processes in the system.
* In addition, collect detailed information for procName if it
* is non-NULL.
*
* Parameters:
* procName name of program to get detailed information for
* all gets usage for all processes
* proc gets usage for a specific process
*/
void
GetProcInfo(char *procName, PROGRAM **all, PROGRAM **proc)
{
struct dirent *dent;
char procFile[MAXPATHLEN];
int fd, i;
struct prpsinfo info;
register prmap_sgi_t *map, *amap;
prmap_sgi_t maps[MAXMAPS];
prmap_sgi_arg_t maparg;
unsigned nmaps;
PROGRAM *list = NULL;
PROGRAM *plist = NULL;
char mapObjName[MAXPATHLEN + 20], *mname, *flagName;
int status;
struct rminfo rminfo;
struct minfo minfo;
long userTotal, pteTotal, uspace, ptespace, wrss, kmem, mem, privSize;
long weighted, refCount;
maparg.pr_vaddr = (caddr_t)maps;
maparg.pr_size = sizeof maps;
OpenProcDir();
userTotal = pteTotal = 0;
while ((dent = readdir(dirp)) != NULL) {
if (!isdigit(dent->d_name[0]))
continue;
sprintf(procFile, "%s/%s", PROCDIR, dent->d_name);
status = (fd = open(procFile, O_RDONLY)) != -1
&& ioctl(fd, PIOCPSINFO, &info) != -1
&& (nmaps = ioctl(fd, PIOCMAP_SGI, &maparg)) != -1;
close(fd);
if (!status) {
continue;
}
#define pr_rss pr_fill[1]
wrss = 0;
privSize = 0;
/* Compute weighted rss from valid and usage counts */
for (map = maps, i = nmaps; i-- > 0; ++map) {
if ((map->pr_mflags & MA_PHYS) == 0) {
refCount = map->pr_mflags >> MA_REFCNT_SHIFT;
weighted = map->pr_wsize;
weighted *= pgsize; /* use 1KB resolution */
weighted /= MA_WSIZE_FRAC;
weighted /= refCount;
map->pr_rss = (long)weighted;
wrss += weighted;
if ((map->pr_mflags & MA_SHMEM) == 0) {
privSize += (map->pr_psize * pgsize) / refCount;
}
} else {
map->pr_rss = 0;
}
}
userTotal += wrss;
/* Figure out if U-area is in core and charge process for it */
uspace = (info.pr_flag & SULOAD) ? (USIZE * kpagesize / 1024) : 0;
userTotal += uspace;
privSize += uspace;
/* Try and figure out how many page tables are resident in memory */
ptespace = PteAccounting(maps, nmaps);
pteTotal += ptespace;
privSize += ptespace;
list = AddElem(list, info.pr_fname, NULL, NULL, privSize,
(long)wrss + uspace + ptespace,
info.pr_rssize * pgsize + uspace + ptespace,
info.pr_size * pgsize, info.pr_pid, 1);
if (procName && strcmp(procName, info.pr_fname) == 0) {
amap = 0;
/*
* Find program text region, so we can get good names for
* the regions associated with each executable file even
* if that file isn't in our inode map.
*/
for (map = maps, i = nmaps; i-- > 0; ++map) {
if (isprgtxt(map)) {
amap = map;
break;
}
}
/* List all the address space segments in virtual order */
for (map = maps, i = nmaps; i-- > 0; ++map) {
if (isdevzero(map) && isrldbss(map)) {
mname = "rld";
map->pr_mflags |= MA_BREAK;
} else if ((map->pr_mflags & MA_STACK) ||
(map->pr_mflags & MA_BREAK) ||
(map->pr_dev == amap->pr_dev && map->pr_ino ==
amap->pr_ino)) {
mname = procName;
} else {
mname = map->pr_ino ?
InodeLookup(map->pr_dev, map->pr_ino) : NULL;
}
flagName = MapFlags(map);
if (mname) {
sprintf(mapObjName, "%s (%s)", flagName, mname);
if (strlen(mapObjName) > 15) {
mapObjName[15] = ')';
mapObjName[16] = '\0';
}
}
refCount = map->pr_mflags >> MA_REFCNT_SHIFT;
plist = AddElem(plist, mname ? mapObjName : flagName,
mname, flagName,
((map->pr_mflags & MA_PHYS) ||
(map->pr_mflags & MA_SHMEM)) ?
0 : (map->pr_psize * pgsize) / refCount,
map->pr_rss,
(map->pr_vsize * pgsize) / refCount,
(map->pr_size + 1023) / 1024, nmaps - i, 0);
}
/* Add U area & PTE space at the bottom */
mem = uspace + ptespace;
plist = AddElem(plist, "U area & PTEs", "U area & PTEs",
IRIX, mem, mem, mem, mem, nmaps+1, 0);
}
}
/*
* Special case if procName is "Irix". We get info from the sysmp
* call.
*/
if (procName && strcmp(procName, IRIX) == 0) {
KernelInfo();
if (sysmp(MP_SAGET, MPSA_RMINFO, &rminfo, sizeof rminfo) == -1
||sysmp(MP_SAGET, MPSA_MINFO, &minfo, sizeof minfo) == -1) {
perror("sysmp");
} else {
i = 0;
kmem = (rminfo.physmem - rminfo.availrmem) * pgsize;
kmem -= pteTotal;
mem = (rminfo.physmem - rminfo.freemem) * pgsize
- (userTotal + kmem + pteTotal) + rminfo.bufmem * pgsize;
plist = AddElem(plist, "FS Cache", "FS Cache", IRIX, mem, mem,
0, 0, i++, 0);
kmem -= rminfo.bufmem * pgsize;
/*
* Subtract streams from heap, since streams memory is
* part of the heap
*/
mem = minfo.heapmem / 1024 - rminfo.strmem * pgsize;
plist = AddElem(plist, "Heap", "Heap", IRIX,
mem, mem, 0, 0, i++, 0);
kmem -= mem;
mem = rminfo.strmem * pgsize;
plist = AddElem(plist, "Streams", "Streams", IRIX,
mem, mem, 0, 0, i++, 0);
kmem -= mem;
mem = minfo.zonemem / 1024;
plist = AddElem(plist, "Zone", "Zone", IRIX,
mem, mem, 0, 0, i++, 0);
kmem -= mem;
mem = minfo.bsdnet / 1024;
plist = AddElem(plist, "BSD Networking",
"BSD Networking", IRIX,
mem, mem, 0, 0, i++, 0);
kmem -= mem;
mem = ketext - kpstart; /* Kernel Text */
mem += kend - ketext; /* Kernel Data */
if (kpbase > kend) {
mem += kpbase - kpfdat; /* Page Frames */
mem += kpfdat - kend; /* Kernel Tables */
}
mem /= 1024;
kmem -= mem;
plist = AddElem(plist, "Other", "Other", IRIX,
kmem, kmem, 0, 0, i++, 0);
if (kpbase > kend) {
mem = (kpbase - kpfdat) / 1024;
plist = AddElem(plist, "Page Frame Data", "Page Frame Data",
IRIX, mem, mem, 0, 0, i++, 0);
mem = (kpfdat - kend) / 1024;
plist = AddElem(plist, "Kernel Tables", "Kernel Tables",
IRIX, mem, mem, 0, 0, i++, 0);
}
mem = (kend - ketext) / 1024;
plist = AddElem(plist, "Unix Data Space", "Unix Data Space",
IRIX, mem, mem, 0, 0, i++, 0);
if ((ktext - kpstart) > 2*NBPP) {
mem = (ketext - ktext) / 1024;
plist = AddElem(plist, "Unix Code Space", "Unix Code Space",
IRIX, mem, mem, 0, 0, i++, 0);
mem = (ktext - kpstart) / 1024;
plist = AddElem(plist, "Symmon", "Symmon", IRIX,
mem, mem, 0, 0, i++, 0);
} else {
mem = (ketext - kpstart) / 1024;
plist = AddElem(plist, "Unix Code Space", "Unix Code Space",
IRIX, mem, mem, 0, 0, i++, 0);
}
}
}
*all = list;
*proc = plist;
}
/*
* void
* FreeUsage(PROGRAM *usage)
*
* Description:
* Free all the memory used by a usage list.
*
* Parameters:
* usage
*/
void
FreeUsage(PROGRAM *usage)
{
PROGRAM *next;
while (usage) {
next = usage->next;
free(usage->progName);
if (usage->mapName) {
free(usage->mapName);
}
free(usage);
usage = next;
}
}